/** @file    Permission.cpp
 *  @author  Alessandro Polo
 *  @version $Id: Permission.cpp 2835 2010-08-03 01:28:00Z alex $
 *  @brief
 * File containing methods for the wosh::Permission class.
 * The header for this class can be found in Permission.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/Permission.h>
 #include <core/ObjectAllocator.h>
 #include <core/SecurityToken.h>
 #include <core/Utilities.h>


using namespace std;
namespace wosh {

 WOSH_REGISTER(wosh::Permission, "wosh::ISerializable", 1.01, _static_Permission)

const Permission Permission::EmptyPermission;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

Permission::Permission() {
	clear();
 }

Permission::Permission( const std::string& mask ) {
	clear();
	setMask( mask );
 }

Permission::Permission( Permission::PERMISSION_TYPE all_mask ) {
	clear();
	setMask( all_mask, all_mask, all_mask );
 }

Permission::Permission( Permission::PERMISSION_TYPE owner, Permission::PERMISSION_TYPE group, Permission::PERMISSION_TYPE others ) {
	clear();
	setMask( owner, group, others );
 }

Permission::Permission( const std::string& ownername, const std::string& groupname, Permission::PERMISSION_TYPE owner, Permission::PERMISSION_TYPE group, Permission::PERMISSION_TYPE others ) {
	clear();
	setOwner( ownername );
	setGroup( groupname );
	setMask( owner, group, others );
 }

Permission::Permission( const std::string& ownername, const std::string& groupname, Permission::PERMISSION_TYPE all_mask ) {
	clear();
	setOwner( ownername );
	setGroup( groupname );
	setMask( all_mask, all_mask, all_mask );
 }

Permission::Permission( const Permission& m) {
	clear();
	*this = m;
 }

Permission::Permission( const Permission& m, Permission::PERMISSION_TYPE all_mask ) {
	clear();
	set(m, all_mask);
 }

Permission::Permission( const Permission& m, Permission::PERMISSION_TYPE owner, Permission::PERMISSION_TYPE group, Permission::PERMISSION_TYPE others ) {
	clear();
	set(m, owner, group, others);
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Permission::clear() {
	this->user = "";
	this->group = "";
	this->mask[0] = static_cast<short>(Permission::None);
	this->mask[1] = static_cast<short>(Permission::None);
	this->mask[2] = static_cast<short>(Permission::None);

	this->access[0] = static_cast<short>(SecurityToken::ACCESS_ANY);
	this->access[1] = static_cast<short>(SecurityToken::ACCESS_ANY);
	this->access[2] = static_cast<short>(SecurityToken::ACCESS_ANY);
 }

bool Permission::isEmpty() const {
	if ( this->user != "" || this->group != "" ) return false;
	if ( this->mask[0] + this->mask[1] + this->mask[2] != 0 ) return false;
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// SETTERS

void Permission::set( const std::string& owner, const std::string& group ) {
	setOwner( owner );
	setGroup( group );
 }

void Permission::set( const std::string& owner, const std::string& group, Permission::PERMISSION_TYPE all_mask ) {
	setOwner( owner );
	setGroup( group );
	setMask( all_mask, all_mask, all_mask );
 }

void Permission::set( const Permission& m, Permission::PERMISSION_TYPE all_mask ) {
	*this = m;
	setMask( all_mask, all_mask, all_mask );
 }

void Permission::set( const Permission& m, Permission::PERMISSION_TYPE owner, Permission::PERMISSION_TYPE group, Permission::PERMISSION_TYPE others ) {
	*this = m;
	if ( owner != Permission::None )
		this->mask[0] = owner;
	if ( group != Permission::None )
		this->mask[1] = group;
	if ( others != Permission::None )
		this->mask[2] = others;
 }

void Permission::inherit( const Permission& value, short overwrite_mode, bool only_when_empty ) {
	if ( overwrite_mode == Permission::Overwrite_None ) return;
	if ( overwrite_mode && Permission::Overwrite_Owner ) {
		if ( !only_when_empty || this->user.empty() )
			this->user = value.user;
	 }
	if ( overwrite_mode && Permission::Overwrite_Group ) {
		if ( !only_when_empty || this->group.empty() )
			this->group = value.group;
	 }
	if ( overwrite_mode && Permission::Overwrite_MaskOwner ) {
		if ( !only_when_empty || this->mask[0] == Permission::None )
			this->mask[0] = value.mask[0];
	 }
	if ( overwrite_mode && Permission::Overwrite_MaskGroup ) {
		if ( !only_when_empty || this->mask[1] == Permission::None )
			this->mask[1] = value.mask[1];
	 }
	if ( overwrite_mode && Permission::Overwrite_MaskOthers ) {
		if ( !only_when_empty || this->mask[2] == Permission::None )
			this->mask[2] = value.mask[2];
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Permission::setNobody() {
	setMask( Permission::Read, Permission::Read, Permission::Read );
	setOwner( "nobody" );
	setGroup( "nobody" );
 }

void Permission::setInheritedIfEmpty( const Permission* parent_perm ) {
	if ( parent_perm == NULL ) {
		setNobody();
		return;
	 }
	if ( this->isEmpty() ) {
		*this = *parent_perm;
	 }
	else {
		if ( this->user.empty() )
			setOwner( parent_perm->getOwner() );
		if ( this->group.empty() )
			setGroup( parent_perm->getGroup() );
	}
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Permission::setMask( const std::string& mask_ ) {
	if ( mask_.size() == 3 ) {
		this->mask[0] = Utilities::fromString<int>( mask_.substr(0,1) );
		this->mask[1] = Utilities::fromString<int>( mask_.substr(1,1) );
		this->mask[2] = Utilities::fromString<int>( mask_.substr(2,3));
	  }
	else if ( mask_.size() == 9 ) {
		this->mask[0] = convertCharToPermission( mask_.substr(0,2) );
		this->mask[1] = convertCharToPermission( mask_.substr(3,5) );
		this->mask[2] = convertCharToPermission( mask_.substr(6,9));
	  }
 }

void Permission::setMask( Permission::PERMISSION_TYPE owner, Permission::PERMISSION_TYPE group, Permission::PERMISSION_TYPE others ) {
	this->mask[0] = owner;
	this->mask[1] = group;
	this->mask[2] = others;
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// SETTERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// GETTERS

short Permission::getMask( unsigned short mask_index ) const {
	if ( mask_index > 2 ) return -1;
	return this->mask[mask_index];
 }

std::string Permission::getMask() const {
	std::string temp;
	temp.push_back( this->mask[0]+48 );
	temp.push_back( this->mask[1]+48 );
	temp.push_back( this->mask[1]+48 );
	return temp;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS

Permission& Permission::operator=(const Permission& m) {
	if (this == &m) return *this; // same object?
	this->user = m.user;
	this->group = m.group;
	this->mask[0] = m.mask[0];
	this->mask[1] = m.mask[1];
	this->mask[2] = m.mask[2];
	return *this;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool Permission::operator==(const Permission &other) const {
	if ( this->user != other.user ) return false;
	if ( this->group != other.group ) return false;
	if ( this->mask[0] != other.mask[0] || this->mask[1] != other.mask[1] || this->mask[2] != other.mask[2]  ) return false;
	return true;
 }

/////////////////////////////////////////////////////////////////////////////////////////////////// OPERATORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// HELPERS

std::string Permission::convertPermissionToChar( Permission::PERMISSION_TYPE mask ) { // RWX
	std::string data = "---";
	if ( (short)mask == 7 ) {
		data[0] = 'r';
		data[1] = 'w';
		data[2] = 'x';
		return data;
	 }
	if ( (short)mask == 4 || (short)mask == 5 || (short)mask == 6 )	data[0] = 'r';
	if ( (short)mask == 2 || (short)mask == 3 || (short)mask == 6 )	data[1] = 'w';
	if ( (short)mask == 1 || (short)mask == 3 || (short)mask == 5 )	data[2] = 'x';
	return data;
 }

Permission::PERMISSION_TYPE Permission::convertCharToPermission( const std::string& data ) { // RWX
	if ( data.size() < 3 ) return (Permission::PERMISSION_TYPE)0;
	short mask = 0;
	if ( data[0] == 'r' )	mask += 4;
	if ( data[1] == 'w' )	mask += 2;
	if ( data[1] == 'x' )	mask += 1;
	return (Permission::PERMISSION_TYPE)mask;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Permission::fromString( const std::string& value ) {
	if ( value.size() >= 9 )
		setMask(value.substr(0,9));
	if ( value.size() < 11 )
		return;
	std::size_t pos = value.find(":");
	if ( pos != std::string::npos )
		setOwner(value.substr(9,pos));
	setGroup(value.substr(pos));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string Permission::toUnixStyle() const {
	return 		convertPermissionToChar( (Permission::PERMISSION_TYPE)this->mask[0] ) + 
				convertPermissionToChar( (Permission::PERMISSION_TYPE)this->mask[1] ) + 
				convertPermissionToChar( (Permission::PERMISSION_TYPE)this->mask[2] ) +
				" " + this->user + " " + this->group;
 }

unsigned int Permission::toUnixStyle_size() const {
	return 3+3+3 + 1 + this->user.size() + 1 + this->group.size();
 }

std::string Permission::toString() const {
	return Utilities::format("%s[%d%d%d,%s:%s]", getClassNameOnlyOf(getClassName()), this->mask[0],this->mask[1],this->mask[2],this->user.c_str(),this->group.c_str() );
 }

bool Permission::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = hash_context->update( this->user ) & ret;
	ret = hash_context->update( this->group ) & ret;
	ret = hash_context->update( this->mask[0] ) & ret;
	ret = hash_context->update( this->mask[1] ) & ret;
	ret = hash_context->update( this->mask[2] ) & ret;
	return ret;
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// HELPERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
